参数装饰器

概述

在 TypeScript 中,装饰器是一种特殊类型的声明,它可以附加到类声明、方法、访问器、属性或参数上。装饰器提供了一种方式来修改或扩展类的行为,而无需修改类的源代码。参数装饰器是装饰器的一种,专门用于修饰函数或方法的参数。

参数装饰器的基本概念

参数装饰器可以用来观察、修改或替换参数的定义。它们通常用于日志记录、验证、依赖注入等场景。参数装饰器不能直接修改参数的值,但可以通过修改类的元数据来影响参数的行为。

参数装饰器的语法

参数装饰器是一个函数,它接收三个参数:

  1. target: 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象。
  2. propertyKey: 成员的名字(字符串或符号)。
  3. parameterIndex: 参数在函数参数列表中的索引。

参数装饰器的返回值会被忽略。

示例代码

function logParameter(target: any, propertyKey: string, parameterIndex: number) {
    console.log(`Decorating parameter ${parameterIndex} of method ${propertyKey} in class ${target.constructor.name}`);
}

class Example {
    greet(@logParameter name: string) {
        console.log(`Hello, ${name}!`);
    }
}

const example = new Example();
example.greet("Alice");

输出

Decorating parameter 0 of method greet in class Example
Hello, Alice!

在这个例子中,logParameter 是一个参数装饰器,它会在 greet 方法的 name 参数上被调用。装饰器函数会输出参数的索引和方法的名称。

参数装饰器的应用场景

1. 日志记录

参数装饰器可以用于记录方法调用时的参数信息,这在调试和日志记录中非常有用。

function logParameter(target: any, propertyKey: string, parameterIndex: number) {
    const originalMethod = target[propertyKey];

    target[propertyKey] = function (...args: any[]) {
        console.log(`Parameter at index ${parameterIndex}:`, args[parameterIndex]);
        return originalMethod.apply(this, args);
    };
}

class Example {
    greet(@logParameter name: string) {
        console.log(`Hello, ${name}!`);
    }
}

const example = new Example();
example.greet("Alice");

2. 参数验证

参数装饰器可以用于验证参数的类型或值,确保它们符合预期的条件。

function validateParameter(target: any, propertyKey: string, parameterIndex: number) {
    const originalMethod = target[propertyKey];

    target[propertyKey] = function (...args: any[]) {
        const param = args[parameterIndex];
        if (typeof param !== 'string' || param.length === 0) {
            throw new Error(`Invalid parameter at index ${parameterIndex}`);
        }
        return originalMethod.apply(this, args);
    };
}

class Example {
    greet(@validateParameter name: string) {
        console.log(`Hello, ${name}!`);
    }
}

const example = new Example();
example.greet(""); // 抛出错误

3. 依赖注入

参数装饰器可以用于实现依赖注入,自动将依赖项注入到方法的参数中。

const dependencies = new Map<string, any>();

function inject(dependencyName: string) {
    return function (target: any, propertyKey: string, parameterIndex: number) {
        const originalMethod = target[propertyKey];

        target[propertyKey] = function (...args: any[]) {
            args[parameterIndex] = dependencies.get(dependencyName);
            return originalMethod.apply(this, args);
        };
    };
}

dependencies.set('logger', console);

class Example {
    greet(@inject('logger') logger: Console) {
        logger.log("Hello, world!");
    }
}

const example = new Example();
example.greet(); // 输出 "Hello, world!"

实践练习

练习 1: 日志记录装饰器

编写一个参数装饰器,用于记录方法调用时的所有参数。

function logAllParameters(target: any, propertyKey: string, parameterIndex: number) {
    const originalMethod = target[propertyKey];

    target[propertyKey] = function (...args: any[]) {
        console.log(`Parameters:`, args);
        return originalMethod.apply(this, args);
    };
}

class Example {
    greet(@logAllParameters name: string, @logAllParameters age: number) {
        console.log(`Hello, ${name}! You are ${age} years old.`);
    }
}

const example = new Example();
example.greet("Alice", 30);

练习 2: 参数验证装饰器

编写一个参数装饰器,用于验证参数是否为正整数。

function validatePositiveInteger(target: any, propertyKey: string, parameterIndex: number) {
    const originalMethod = target[propertyKey];

    target[propertyKey] = function (...args: any[]) {
        const param = args[parameterIndex];
        if (typeof param !== 'number' || param <= 0 || !Number.isInteger(param)) {
            throw new Error(`Invalid parameter at index ${parameterIndex}`);
        }
        return originalMethod.apply(this, args);
    };
}

class Example {
    greet(@validatePositiveInteger age: number) {
        console.log(`You are ${age} years old.`);
    }
}

const example = new Example();
example.greet(-5); // 抛出错误

练习 3: 依赖注入装饰器

编写一个参数装饰器,用于将一个配置对象注入到方法中。

const config = {
    apiUrl: 'https://api.example.com',
    timeout: 5000
};

function injectConfig(target: any, propertyKey: string, parameterIndex: number) {
    const originalMethod = target[propertyKey];

    target[propertyKey] = function (...args: any[]) {
        args[parameterIndex] = config;
        return originalMethod.apply(this, args);
    };
}

class Example {
    fetchData(@injectConfig config: any) {
        console.log(`Fetching data from ${config.apiUrl} with timeout ${config.timeout}`);
    }
}

const example = new Example();
example.fetchData(); // 输出 "Fetching data from https://api.example.com with timeout 5000"

总结

参数装饰器是 TypeScript 中强大的工具,可以用于日志记录、参数验证、依赖注入等多种场景。通过理解和实践参数装饰器,你可以更好地掌握 TypeScript 的高级特性,并在实际项目中灵活应用。

希望这篇教程能帮助你更好地理解和使用参数装饰器。继续探索 TypeScript 的世界,你会发现更多有趣和强大的功能!